home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / passwd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-05  |  19.0 KB  |  852 lines

  1. /*
  2.  * Copyright 1989, 1990, 1991, 1992, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  */
  11.  
  12. #include "config.h"
  13. #include <sys/types.h>
  14. #include <time.h>
  15. #include <stdio.h>
  16. #include <fcntl.h>
  17. #include <signal.h>
  18.  
  19. #ifndef    lint
  20. static    char    sccsid[] = "@(#)passwd.c    3.8    20:37:37    3/7/92";
  21. #endif
  22.  
  23. /*
  24.  * Set up some BSD defines so that all the BSD ifdef's are
  25.  * kept right here 
  26.  */
  27.  
  28. #ifndef    BSD
  29. #include <string.h>
  30. #include <memory.h>
  31. #define    bzero(a,n)    memset(a, 0, n)
  32. #else
  33. #include <strings.h>
  34. #define    strchr    index
  35. #define    strrchr    rindex
  36. #endif
  37.  
  38. #ifdef    STDLIB_H
  39. #include <stdlib.h>
  40. #endif
  41. #ifdef    UNISTD_H
  42. #include <unistd.h>
  43. #endif
  44. #ifdef    ULIMIT_H
  45. #include <ulimit.h>
  46. #endif
  47.  
  48. #ifndef UL_SFILLIM
  49. #define UL_SFILLIM    2
  50. #endif
  51.  
  52. #include "pwd.h"
  53. #include "lastlog.h"
  54. #include "shadow.h"
  55.  
  56. #ifdef    USE_SYSLOG
  57. #include <syslog.h>
  58.  
  59. #ifndef    LOG_WARN
  60. #define    LOG_WARN    LOG_WARNING
  61. #endif
  62. #endif
  63. #ifdef    USE_RLIMIT
  64. #include <sys/resource.h>
  65.  
  66. struct    rlimit    rlimit_fsize = { RLIM_INFINITY, RLIM_INFINIT };
  67. #endif
  68.  
  69. /*
  70.  * Password aging constants
  71.  *
  72.  *    DAY - seconds in a day
  73.  *    WEEK - seconds in a week
  74.  *    SCALE - convert from clock to aging units
  75.  */
  76.  
  77. #ifndef    DAY
  78. #define    DAY    (24L*3600L)
  79. #endif
  80. #define    WEEK    (7L*DAY)
  81.  
  82. #ifdef    ITI_AGING
  83. #define    SCALE    (1)
  84. #else
  85. #define    SCALE    DAY
  86. #endif
  87.  
  88. /*
  89.  * Global variables
  90.  */
  91.  
  92. char    name[32];        /* The user's name */
  93. char    *Prog;            /* Program name */
  94. int    amroot;            /* The real UID was 0 */
  95.  
  96. /*
  97.  * External identifiers
  98.  */
  99.  
  100. extern    char    *getpass();
  101. extern    char    *pw_encrypt();
  102. extern    char    *getlogin();
  103. extern    int    optind;        /* Index into argv[] for current option */
  104. extern    char    *optarg;    /* Pointer to current option value */
  105. #ifdef    NDBM
  106. extern    int    sp_dbm_mode;
  107. extern    int    pw_dbm_mode;
  108. #endif
  109.  
  110. /*
  111.  * #defines for messages.  This facilities foreign language conversion
  112.  * since all messages are defined right here.
  113.  */
  114.  
  115. #define    USAGE        "usage: %s [ -f | -s ] [ name ]\n"
  116. #define    ADMUSAGE \
  117.     "       %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
  118. #define    ADMUSAGE2 \
  119.     "       %s { -l | -d | -S } name\n"
  120. #define    OLDPASS        "Old Password:"
  121. #define    NEWPASSMSG \
  122. "Enter the new password (minimum of %d characters)\n\
  123. Please use a combination of upper and lower case letters and numbers.\n"
  124. #define    CHANGING    "Changing password for %s\n"
  125. #define NEWPASS        "New Password:"
  126. #define    NEWPASS2    "Re-enter new password:"
  127. #define    WRONGPWD    "Incorrect password for %s.\n"
  128. #define    WRONGPWD2    "incorrect password for `%s'\n"
  129. #define    NOMATCH        "They don't match; try again.\n"
  130. #define    CANTCHANGE    "The password for %s cannot be changed.\n"
  131. #define    CANTCHANGE2    "password locked for `%s'\n"
  132. #define    TOOSOON        "Sorry, the password for %s cannot be changed yet.\n"
  133. #define    TOOSOON2    "now < sp_min for `%s'\n"
  134. #define    EXECFAILED    "%s: Cannot execute %s"
  135. #define    EXECFAILED2    "cannot execute %s\n"
  136. #define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  137. #define    UNKUSER        "%s: Unknown user %s\n"
  138. #define    NOPERM        "You may not change the password for %s.\n"
  139. #define    NOPERM2        "can't change pwd for `%s'\n"
  140. #define    UNCHANGED    "The password for %s is unchanged.\n"
  141. #define    SPWDBUSY    "Cannot lock the password file; try again later.\n"
  142. #define    SPWDBUSY2    "can't lock /etc/shadow\n"
  143. #define    OPNERROR    "Cannot open the password file.\n"
  144. #define    OPNERROR2    "can't open /etc/shadow\n"
  145. #define    UPDERROR    "Error updating the password entry.\n"
  146. #define    UPDERROR2    "error updating shadow entry\n"
  147. #define    DBMERROR    "Error updating the DBM password entry.\n"
  148. #define    DBMERROR2    "error updating DBM shadow entry.\n"
  149. #define    NOTROOT        "Cannot change ID to root.\n"
  150. #define    NOTROOT2    "can't setuid(0).\n"
  151. #define    CLSERROR    "Cannot commit shadow file changes.\n"
  152. #define    CLSERROR2    "can't rewrite /etc/shadow.\n"
  153. #define    UNLKERROR    "Cannot unlock the shadow file.\n"
  154. #define    UNLKERROR2    "can't unlock /etc/shadow.\n"
  155. #define    TRYAGAIN    "Try again.\n"
  156. #define    CHGPASSWD    "changed password for `%s'\n"
  157.  
  158. /*
  159.  * usage - print command usage and exit
  160.  */
  161.  
  162. void
  163. usage ()
  164. {
  165.     fprintf (stderr, USAGE, Prog);
  166.     if (amroot) {
  167.         fprintf (stderr, ADMUSAGE, Prog);
  168.         fprintf (stderr, ADMUSAGE2, Prog);
  169.     }
  170.     exit (1);
  171. }
  172.  
  173. /*
  174.  * new_password - validate old password and replace with new
  175.  */
  176.  
  177. /*ARGSUSED*/
  178. int
  179. new_password (pw, sp)
  180. struct    passwd    *pw;
  181. struct    spwd    *sp;
  182. {
  183.     char    *clear;        /* Pointer to clear text */
  184.     char    *cipher;    /* Pointer to cipher text */
  185.     char    *cp;        /* Pointer to getpass() response */
  186.     char    orig[BUFSIZ];    /* Original password */
  187.     char    pass[BUFSIZ];    /* New password */
  188.     int    i;        /* Counter for retries */
  189.  
  190.     /*
  191.      * Authenticate the user.  The user will be prompted for their
  192.      * own password.
  193.      */
  194.  
  195.     if (! amroot && sp->sp_pwdp[0]) {
  196.         bzero (orig, sizeof orig);
  197.  
  198.         if (! (clear = getpass (OLDPASS)))
  199.             return -1;
  200.  
  201.         cipher = pw_encrypt (clear, sp->sp_pwdp);
  202.         if (strcmp (cipher, sp->sp_pwdp) != 0) {
  203.             sleep (1);
  204.             fprintf (stderr, WRONGPWD, sp->sp_namp);
  205. #ifdef    USE_SYSLOG
  206.             syslog (LOG_WARN, WRONGPWD2, sp->sp_namp);
  207. #endif
  208.             return -1;
  209.         }
  210.         strcpy (orig, clear);
  211.         bzero (cipher, strlen (cipher));
  212.         bzero (clear, strlen (clear));
  213.     }
  214.  
  215.     /*
  216.      * Get the new password.  The user is prompted for the new password
  217.      * and has three tries to get it right.  The password will be tested
  218.      * for strength, unless it is the root user.  This provides an escape
  219.      * for initial login passwords.
  220.      */
  221.  
  222.     printf (NEWPASSMSG, getdef_num ("PASS_MIN_LEN", 5));
  223.     for (i = 0;i < 3;i++) {
  224.         if (! (cp = getpass (NEWPASS))) {
  225.             bzero (orig, sizeof orig);
  226.             return -1;
  227.         } else
  228.             strcpy (pass, cp);
  229.  
  230.         if (! amroot && ! obscure (orig, pass)) {
  231.             printf (TRYAGAIN);
  232.             continue;
  233.         }
  234.         if (! (cp = getpass (NEWPASS2))) {
  235.             bzero (orig, sizeof orig);
  236.             return -1;
  237.         }
  238.         if (strcmp (cp, pass))
  239.             fprintf (stderr, NOMATCH);
  240.         else {
  241.             bzero (cp, strlen (cp));
  242.             break;
  243.         }
  244.     }
  245.     bzero (orig, sizeof orig);
  246.  
  247.     if (i == 3) {
  248.         bzero (pass, sizeof pass);
  249.         return -1;
  250.     }
  251.  
  252.     /*
  253.      * Encrypt the password.  The new password is encrypted and
  254.      * the shadow password structure updated to reflect the change.
  255.      */
  256.  
  257.     sp->sp_pwdp = pw_encrypt (pass, (char *) 0);
  258.     sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  259.     bzero (pass, sizeof pass);
  260.  
  261.     return 0;
  262. }
  263.  
  264. /*
  265.  * check_password - test a password to see if it can be changed
  266.  *
  267.  *    check_password() sees if the invoker has permission to change the
  268.  *    password for the given user.
  269.  */
  270.  
  271. /*ARGSUSED*/
  272. void
  273. check_password (pw, sp)
  274. struct    passwd    *pw;
  275. struct    spwd    *sp;
  276. {
  277.     time_t    now = time ((time_t *) 0) / SCALE;
  278.  
  279.     /*
  280.      * Root can change any password any time.
  281.      */
  282.  
  283.     if (amroot)
  284.         return;
  285.  
  286.     /*
  287.      * Expired accounts cannot be changed ever.  Passwords
  288.      * which are locked may not be changed.  Passwords where
  289.      * min > max may not be changed.  Passwords which have
  290.      * been inactive too long cannot be changed.
  291.      */
  292.  
  293.     if ((sp->sp_expire > 0 && now >= sp->sp_expire) ||
  294.         (sp->sp_inact >= 0 && sp->sp_max >= 0 &&
  295.         now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) ||
  296.             strcmp (sp->sp_pwdp, "!") == 0 ||
  297.             sp->sp_min > sp->sp_max) {
  298.         fprintf (stderr, CANTCHANGE, sp->sp_namp);
  299. #ifdef    USE_SYSLOG
  300.         syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp);
  301.         closelog ();
  302. #endif
  303.         exit (1);
  304.     }
  305.  
  306.     /*
  307.      * Passwords may only be changed after sp_min time is up.
  308.      */
  309.  
  310.     if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) {
  311.         fprintf (stderr, TOOSOON, sp->sp_namp);
  312. #ifdef    USE_SYSLOG
  313.         syslog (LOG_WARN, TOOSOON2, sp->sp_namp);
  314.         closelog ();
  315. #endif
  316.         exit (1);
  317.     }
  318. }
  319.  
  320. /*
  321.  * pwd_to_spwd - create entries for new spwd structure
  322.  *
  323.  *    pwd_to_spwd() creates a new (struct spwd) containing the
  324.  *    information in the pointed-to (struct passwd).
  325.  */
  326.  
  327. void
  328. pwd_to_spwd (pw, sp)
  329. struct    passwd    *pw;
  330. struct    spwd    *sp;
  331. {
  332.     time_t    t;
  333.  
  334.     /*
  335.      * Nice, easy parts first.  The name and passwd map directly
  336.      * from the old password structure to the new one.
  337.      */
  338.  
  339.     sp->sp_namp = strdup (pw->pw_name);
  340.     sp->sp_pwdp = strdup (pw->pw_passwd);
  341. #ifdef    ATT_AGE
  342.  
  343.     /*
  344.      * AT&T-style password aging maps the sp_min, sp_max, and
  345.      * sp_lstchg information from the pw_age field, which appears
  346.      * after the encrypted password.
  347.      */
  348.  
  349.     if (pw->pw_age[0]) {
  350.         t = (c64i (pw->pw_age[0]) * WEEK) / SCALE;
  351.         sp->sp_max = t;
  352.  
  353.         if (pw->pw_age[1]) {
  354.             t = (c64i (pw->pw_age[1]) * WEEK) / SCALE;
  355.             sp->sp_min = t;
  356.         } else
  357.             sp->sp_min = (10000L * DAY) / SCALE;
  358.  
  359.         if (pw->pw_age[1] && pw->pw_age[2]) {
  360.             t = (a64l (pw->pw_age + 2) * WEEK) / SCALE;
  361.             sp->sp_lstchg = t;
  362.         } else
  363.             sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  364.     } else {
  365.         sp->sp_min = 0;
  366.         sp->sp_max = (10000L * DAY) / SCALE;
  367.         sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  368.     }
  369. #else
  370.     /*
  371.      * BSD does not use the pw_age field and has no aging information
  372.      * anywheres.  The default values are used to initialize the
  373.      * fields which are in the missing pw_age field;
  374.      */
  375.  
  376.     sp->sp_min = 0;
  377.     sp->sp_max = (10000L * DAY) / SCALE;
  378.     sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  379. #endif
  380.  
  381.     /*
  382.      * These fields have no corresponding information in the password
  383.      * file.  They are set to uninitialized values.
  384.      */
  385.  
  386.     sp->sp_warn = -1;
  387.     sp->sp_inact = -1;
  388.     sp->sp_expire = -1;
  389.     sp->sp_flag = -1;
  390. }
  391.  
  392. /*
  393.  * print_status - print current password status
  394.  */
  395.  
  396. /*ARGSUSED*/
  397. void
  398. print_status (pw, sp)
  399. struct    passwd    *pw;
  400. struct    spwd    *sp;
  401. {
  402.     struct    tm    *tm;
  403.     time_t    last_time;
  404.  
  405.     last_time = sp->sp_lstchg * SCALE;
  406.     tm = gmtime (&last_time);
  407.  
  408.     printf ("%s ", sp->sp_namp);
  409.     printf ("%s ",
  410.         sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP");
  411.     printf ("%02.2d/%02.2d/%02.2d ",
  412.         tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
  413.     printf ("%d %d %d %d\n",
  414.         (sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY,
  415.         (sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY);
  416. }
  417.  
  418. /*
  419.  * passwd - change a user's password file information
  420.  *
  421.  *    This command controls the password file and commands which are
  422.  *     used to modify it.
  423.  *
  424.  *    The valid options are
  425.  *
  426.  *    -l    lock the named account (*)
  427.  *    -d    delete the password for the named account (*)
  428.  *    -x #    set sp_max to # days (*)
  429.  *    -n #    set sp_min to # days (*)
  430.  *    -w #    set sp_warn to # days (*)
  431.  *    -i #    set sp_inact to # days (*)
  432.  *    -S    show password status of named account (*)
  433.  *    -g    execute gpasswd command to interpret flags
  434.  *    -f    execute chfn command to interpret flags
  435.  *    -s    execute chsh command to interpret flags
  436.  *
  437.  *    (*) requires root permission to execute.
  438.  *
  439.  *    All of the time fields are entered in days and converted to the
  440.  *     appropriate internal format.  For finer resolute the chage
  441.  *    command must be used.
  442.  */
  443.  
  444. int
  445. main (argc, argv)
  446. int    argc;
  447. char    **argv;
  448. {
  449.     char    buf[BUFSIZ];        /* I/O buffer for messages, etc.      */
  450.     char    new_passwd[BUFSIZ];    /* Buffer for changed passwords       */
  451.     char    *cp;            /* Miscellaneous character pointing   */
  452.     time_t    min;            /* Minimum days before change         */
  453.     time_t    max;            /* Maximum days until change          */
  454.     time_t    warn;            /* Warning days before change         */
  455.     time_t    inact;            /* Days without change before locked  */
  456.     int    i;            /* Loop control variable              */
  457.     int    flag;            /* Current option to process          */
  458.     int    lflg = 0;        /* -l - lock account option           */
  459.     int    uflg = 0;        /* -u - unlock account option         */
  460.     int    dflg = 0;        /* -d - delete password option        */
  461.     int    xflg = 0;        /* -x - set maximum days              */
  462.     int    nflg = 0;        /* -n - set minimum days              */
  463.     int    wflg = 0;        /* -w - set warning days              */
  464.     int    iflg = 0;        /* -i - set inactive days             */
  465.     int    Sflg = 0;        /* -S - show password status          */
  466.     struct    passwd    *pw;        /* Password file entry for user       */
  467.     struct    spwd    *sp;        /* Shadow file entry for user         */
  468.     struct    spwd    tspwd;        /* New shadow file entry if none      */
  469.  
  470.     /*
  471.      * The program behaves differently when executed by root
  472.      * than when executed by a normal user.
  473.      */
  474.  
  475.     amroot = getuid () == 0;
  476. #ifdef    NDBM
  477.     sp_dbm_mode = O_RDWR;
  478.     pw_dbm_mode = O_RDWR;
  479. #endif
  480.  
  481.     /*
  482.      * Get the program name.  The program name is used as a
  483.      * prefix to most error messages.  It is also used as input
  484.      * to the openlog() function for error logging.
  485.      */
  486.  
  487.     if (Prog = strrchr (argv[0], '/'))
  488.         Prog++;
  489.     else
  490.         Prog = argv[0];
  491.  
  492. #ifdef    USE_SYSLOG
  493.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  494. #endif
  495.  
  496.     /*
  497.      * Start with the flags which cause another command to be
  498.      * executed.  The effective UID will be set back to the
  499.      * real UID and the new command executed with the flags
  500.      */
  501.  
  502.     if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
  503.         setuid (getuid ());
  504.         switch (argv[1][1]) {
  505.             case 'g':
  506.                 argv[1] = "gpasswd";
  507.                 execv ("/bin/gpasswd", &argv[1]);
  508.                 break;
  509.             case 'f':
  510.                 argv[1] = "chfn";
  511.                 execv ("/bin/chfn", &argv[1]);
  512.                 break;
  513.             case 's':
  514.                 argv[1] = "chsh";
  515.                 execv ("/bin/chsh", &argv[1]);
  516.                 break;
  517.             default:
  518.                 usage ();
  519.         }
  520.         sprintf (buf, EXECFAILED, Prog, argv[1]);
  521.         perror (buf);
  522. #ifdef    USE_SYSLOG
  523.         syslog (LOG_CRIT, EXECFAILED2, argv[1]);
  524.         closelog ();
  525. #endif
  526.         exit (1);
  527.     }
  528.  
  529.     /* 
  530.      * The remaining arguments will be processed one by one and
  531.      * executed by this command.  The name is the last argument
  532.      * if it does not begin with a "-", otherwise the name is
  533.      * determined from the environment and must agree with the
  534.      * real UID.  Also, the UID will be checked for any commands
  535.      * which are restricted to root only.
  536.      */
  537.  
  538.     while ((flag = getopt (argc, argv, "ludx:n:w:i:S")) != EOF) {
  539.         switch (flag) {
  540.             case 'x':
  541.                 max = strtol (optarg, &cp, 10);
  542.                 if (*cp || getuid ())
  543.                     usage ();
  544.  
  545.                 xflg++;
  546.                 break;
  547.             case 'n':
  548.                 min = strtol (optarg, &cp, 10);
  549.                 if (*cp || getuid ())
  550.                     usage ();
  551.  
  552.                 nflg++;
  553.                 break;
  554.             case 'w':
  555.                 warn = strtol (optarg, &cp, 10);
  556.                 if (*cp || getuid ())
  557.                     usage ();
  558.  
  559.                 wflg++;
  560.                 break;
  561.             case 'i':
  562.                 inact = strtol (optarg, &cp, 10);
  563.                 if (*cp || getuid ())
  564.                     usage ();
  565.  
  566.                 iflg++;
  567.                 break;
  568.             case 'S':
  569.                 if (getuid ())
  570.                     usage ();
  571.  
  572.                 Sflg++;
  573.                 break;
  574.             case 'd':
  575.                 if (getuid ())
  576.                     usage ();
  577.  
  578.                 dflg++;
  579.                 break;
  580.             case 'l':
  581.                 if (getuid ())
  582.                     usage ();
  583.  
  584.                 lflg++;
  585.                 break;
  586.             case 'u':
  587.                 uflg++;
  588.                 break;
  589.             default:
  590.                 usage ();
  591.         }
  592.     }
  593.  
  594.     /*
  595.      * If any of the flags were given, a user name must be supplied
  596.      * on the command line.  Only an unadorned command line doesn't
  597.      * require the user's name be given.  Also, on -x, -n, -m, and
  598.      * -i may appear with each other.  -d, -l and -S must appear alone.
  599.      */
  600.  
  601.     if ((dflg || lflg || xflg || nflg ||
  602.                 wflg || iflg || Sflg) && optind >= argc)
  603.         usage ();
  604.  
  605.     if ((dflg + lflg + uflg + (xflg || nflg || wflg || iflg) + Sflg) > 1)
  606.         usage ();
  607.  
  608.     /*
  609.      * Now I have to get the user name.  The name will be gotten 
  610.      * from the command line if possible.  Otherwise it is figured
  611.      * out from the environment.
  612.      */
  613.  
  614.     if (optind < argc) {
  615.         strncpy (name, argv[optind], sizeof name);
  616.         name[sizeof name - 1] = '\0';
  617.     } else if (amroot) {
  618.         strcpy (name, "root");
  619.     } else if (cp = getlogin ()) {
  620.         strncpy (name, cp, sizeof name);
  621.         name[sizeof name - 1] = '\0';
  622.     } else {
  623.         fprintf (stderr, WHOAREYOU, Prog);
  624. #ifdef    USE_SYSLOG
  625.         closelog ();
  626. #endif
  627.         exit (1);
  628.     }
  629.  
  630.     /*
  631.      * Now I have a name, let's see if the UID for the name
  632.      * matches the current real UID.
  633.      */
  634.  
  635.     if (! (pw = getpwnam (name))) {
  636.         fprintf (stderr, UNKUSER, Prog, name);
  637. #ifdef    USE_SYSLOG
  638.         closelog ();
  639. #endif
  640.         exit (1);
  641.     }
  642.     if (! amroot && pw->pw_uid != getuid ()) {
  643.         fprintf (stderr, NOPERM, name);
  644. #ifdef    USE_SYSLOG
  645.         syslog (LOG_WARN, NOPERM2, name);
  646.         closelog ();
  647. #endif
  648.         exit (1);
  649.     }
  650.  
  651.     /*
  652.      * Let the user know whose password is being changed.
  653.      */
  654.  
  655.     if (! Sflg)
  656.         printf (CHANGING, name);
  657.  
  658.     /*
  659.      * The user name is valid, so let's get the shadow file
  660.      * entry.
  661.      */
  662.  
  663.     if (! (sp = getspnam (name)))
  664.         pwd_to_spwd (pw, sp = &tspwd);
  665.  
  666.     /*
  667.      * Save the shadow entry off to the side so it doesn't
  668.      * get changed by any of the following code.
  669.      */
  670.  
  671.     if (sp != &tspwd) {
  672.         tspwd = *sp;
  673.         sp = &tspwd;
  674.     }
  675.     tspwd.sp_namp = strdup (sp->sp_namp);
  676.     tspwd.sp_pwdp = strdup (sp->sp_pwdp);
  677.  
  678.     if (Sflg) {
  679.         print_status (pw, sp);
  680. #ifdef    USE_SYSLOG
  681.         closelog ();
  682. #endif
  683.         exit (0);
  684.     }
  685.  
  686.     /*
  687.      * If there are no other flags, just change the password.
  688.      */
  689.  
  690.     if (! (dflg || lflg || uflg || xflg || nflg || wflg || iflg)) {
  691.  
  692.         /*
  693.          * See if the user is permitted to change the password.
  694.          * Otherwise, go ahead and set a new password.
  695.          */
  696.  
  697.         check_password (pw, sp);
  698.  
  699.         if (new_password (pw, sp)) {
  700.             fprintf (stderr, UNCHANGED, name);
  701. #ifdef    USE_SYSLOG
  702.             closelog ();
  703. #endif
  704.             exit (1);
  705.         }
  706.     }
  707.  
  708.     /*
  709.      * The other options are incredibly simple.  Just modify the
  710.      * field in the shadow file entry.
  711.      */
  712.  
  713.     if (dflg)            /* Set password to blank */
  714.         sp->sp_pwdp = "";
  715.  
  716.     if (lflg) {            /* Set password to "locked" value */
  717.         if (sp->sp_pwdp && sp->sp_pwdp[0] != '!') {
  718.             strcpy (new_passwd, "!");
  719.             strcat (new_passwd, sp->sp_pwdp);
  720.             sp->sp_pwdp = new_passwd;
  721.         }
  722.     }
  723.     if (uflg) {            /* Undo password "locked" value */
  724.         if (sp->sp_pwdp && sp->sp_pwdp[0] == '!') {
  725.             strcpy (new_passwd, sp->sp_pwdp + 1);
  726.             sp->sp_pwdp = new_passwd;
  727.         }
  728.     }
  729.     if (xflg)
  730.         sp->sp_max = (max * DAY) / SCALE;
  731.  
  732.     if (nflg)
  733.         sp->sp_min = (min * DAY) / SCALE;
  734.  
  735.     if (wflg)
  736.         sp->sp_warn = (warn * DAY) / SCALE;
  737.  
  738.     if (iflg)
  739.         sp->sp_inact = (inact * DAY) / SCALE;
  740.  
  741.     /*
  742.      * Before going any further, raise the ulimit to prevent
  743.      * colliding into a lowered ulimit, and set the real UID
  744.      * to root to protect against unexpected signals.  Any
  745.      * keyboard signals are set to be ignored.
  746.      */
  747.  
  748. #ifdef    HAVE_ULIMIT
  749.     ulimit (UL_SFILLIM, 30000);
  750. #endif
  751. #ifdef    HAVE_RLIMIT
  752.     setrlimit (RLIMIT_FSIZE, &rlimit_fsize);
  753. #endif
  754.     if (setuid (0)) {
  755.         fprintf (stderr, NOTROOT);
  756. #ifdef    USE_SYSLOG
  757.         syslog (LOG_ERR, NOTROOT2);
  758.         closelog ();
  759. #endif
  760.         exit (1);
  761.     }
  762.     signal (SIGHUP, SIG_IGN);
  763.     signal (SIGINT, SIG_IGN);
  764.     signal (SIGQUIT, SIG_IGN);
  765. #ifdef    SIGTSTP
  766.     signal (SIGTSTP, SIG_IGN);
  767. #endif
  768.  
  769.     /*
  770.      * The shadow entry is now ready to be committed back to
  771.      * the shadow file.  Get a lock on the file and open it.
  772.      */
  773.  
  774.     for (i = 0;i < 30;i++)
  775.         if (spw_lock ())
  776.             break;
  777.  
  778.     if (i == 30) {
  779.         fprintf (stderr, SPWDBUSY);
  780. #ifdef    USE_SYSLOG
  781.         syslog (LOG_WARN, SPWDBUSY2);
  782.         closelog ();
  783. #endif
  784.         exit (1);
  785.     }
  786.     if (! spw_open (O_RDWR)) {
  787.         fprintf (stderr, OPNERROR);
  788. #ifdef    USE_SYSLOG
  789.         syslog (LOG_ERR, OPNERROR2);
  790.         closelog ();
  791. #endif
  792.         (void) spw_unlock ();
  793.         exit (1);
  794.     }
  795.  
  796.     /*
  797.      * Update the shadow file entry.  If there is a DBM file,
  798.      * update that entry as well.
  799.      */
  800.  
  801.     if (! spw_update (sp)) {
  802.         fprintf (stderr, UPDERROR);
  803. #ifdef    USE_SYSLOG
  804.         syslog (LOG_ERR, UPDERROR2);
  805.         closelog ();
  806. #endif
  807.         (void) spw_unlock ();
  808.         exit (1);
  809.     }
  810. #ifdef    NDBM
  811.     if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
  812.         fprintf (stderr, DBMERROR);
  813. #ifdef    USE_SYSLOG
  814.         syslog (LOG_ERR, DBMERROR2);
  815.         closelog ();
  816. #endif
  817.         (void) spw_unlock ();
  818.         exit (1);
  819.     }
  820.     endspent ();
  821. #endif
  822.  
  823.     /*
  824.      * Changes have all been made, so commit them and unlock the
  825.      * file.
  826.      */
  827.  
  828.     if (! spw_close ()) {
  829.         fprintf (stderr, CLSERROR);
  830. #ifdef    USE_SYSLOG
  831.         syslog (LOG_ERR, CLSERROR2);
  832.         closelog ();
  833. #endif
  834.         (void) spw_unlock ();
  835.         exit (1);
  836.     }
  837.     if (! spw_unlock ()) {
  838.         fprintf (stderr, UNLKERROR);
  839. #ifdef    USE_SYSLOG
  840.         syslog (LOG_ERR, UNLKERROR2);
  841.         closelog ();
  842. #endif
  843.         exit (1);
  844.     }
  845. #ifdef    USE_SYSLOG
  846.     syslog (LOG_INFO, CHGPASSWD, name);
  847.     closelog ();
  848. #endif
  849.     exit (0);
  850.     /*NOTREACHED*/
  851. }
  852.